package editor.syntaxhighlighter;

import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Element;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;

import editor.JavaEditorPane;
import editor.manager.WorkSpaceManager;

/**
 *
 * @author Mark Dessain
 */
@SuppressWarnings("serial")
public class JavaDocument extends DefaultStyledDocument
{
	
	private Element root;
    private JavaEditorPane edit;

    /**
     * @param edit the text-area which is related
     */
    public JavaDocument(JavaEditorPane edit)
    {
        root = getDefaultRootElement();
        this.edit = edit;
    }
    
    /**
     * @param offs where the update begins
     * @param current SyntaxType
     * @return
     */
    private SyntaxType newUpdate(int offs, SyntaxType current)
    {
        String text;

        int start = root.getElementIndex(0);
        int end = root.getElementIndex(this.getLength());

        ArrayList<DefaultStyledDocument.ElementSpec> specs = new ArrayList<DefaultStyledDocument.ElementSpec>();
        MutableAttributeSet attrs = new SimpleAttributeSet();

        try {

            for(int i = start; i <= end; i++)
            {
                int iStart = root.getElement(i).getStartOffset();
                int iEnd = root.getElement(i).getEndOffset();

                if(!(edit.getText(iStart, (iEnd - iStart)).equalsIgnoreCase("\n") && i == end))
                {
                    specs.add(new DefaultStyledDocument.ElementSpec(attrs, DefaultStyledDocument.ElementSpec.EndTagType));
                    specs.add(new DefaultStyledDocument.ElementSpec(attrs, DefaultStyledDocument.ElementSpec.StartTagType));

                    text = edit.getText(iStart, (iEnd - iStart));
                    TokenStateMachine tsm = new TokenStateMachine(text, current, this, attrs);
                    specs.addAll(tsm.setPosition());
                    current = tsm.getEndSyntax();
                }

            }

            DefaultStyledDocument.ElementSpec[] specsArray = new DefaultStyledDocument.ElementSpec[specs.size()];
            specs.toArray(specsArray);

            int length = this.getLength();
            int pos = edit.getCaretPosition();

            super.remove(0, length);
            insert(0, specsArray);
            edit.setCaretPosition(pos);

        } catch (BadLocationException ex) {
            Logger.getLogger(JavaDocument.class.getName()).log(Level.SEVERE, null, ex);
        }

        return current;
    }
    

    /* (non-Javadoc)
     * @see javax.swing.text.AbstractDocument#remove(int, int)
     */
    @Override
    public void remove(int offs, int length) throws BadLocationException
    {
        super.remove(offs, length);
        newUpdate(offs, SyntaxType.NULL);
    }


    /* (non-Javadoc)
     * @see javax.swing.text.AbstractDocument#insertString(int, java.lang.String, javax.swing.text.AttributeSet)
     */
    @Override
    public void insertString(int offs, String str, AttributeSet a) throws BadLocationException 
    {   
    	
    	int backCount = 0;
    	boolean ignore = false;
    	
    	str = str.replace("\t", "     ");
    	StringBuilder builder = new StringBuilder();
    	builder.append(str);
    	
		int lineNumber = root.getElementIndex(offs);
		Element element = root.getElement(lineNumber);

    	if(str.equals("\n"))
    	{
    		spacesBeforeLine(element, builder);
    		
    		int caretIndex = getCaretIndex(element);
    		
    		if(element.getElementCount() > 1 && !(lineNumber == 0 && caretIndex == 0))
    		{
	    		Element caretElement = element.getElement(caretIndex - 1);
	    		
	    		if(caretElement.getAttributes().getAttribute("string").toString().equals("{"))
	    		{
	    			builder.append("     ");
	    		}
    		}
    	}
    	else if(str.equals("{"))
    	{
    		builder.append("\n");
    		spacesBeforeLine(element, builder);
    		builder.append("     ");
    		builder.append("\n");
    		backCount = spacesBeforeLine(element, builder) + 2;
    		builder.append("}");
    	}
    	else if(str.equals("("))
    	{
    		builder.append(")");
    		backCount = 1;
    	}
    	else if(str.equals("["))
    	{
    		builder.append("]");
    		backCount = 1;
    	}
		else if(str.equals("\"") || str.equals("'"))
    	{
    		builder.append(str);
    		backCount = 1;
    	}
    	else if(str.equals("}") || str.equals(")") || str.equals("]"))
		{
    		int caretIndex = getCaretIndex(element);
    		
    		
    		if(element.getElement(caretIndex).getAttributes().getAttribute("string").toString().endsWith(str))
    		{
    			ignore = true;
    			builder = new StringBuilder();
    			backCount = -1;
    		}
		}
   
    	
        super.insertString(offs, builder.toString(), a);
        newUpdate(offs, SyntaxType.NULL);
        
        if(backCount > 0 || backCount < 0 )
        {
        	int caret = WorkSpaceManager.textAreaManager.getSelectedEditor().getCaretPosition();
        	WorkSpaceManager.textAreaManager.getSelectedEditor().setCaretPosition(caret - backCount);
        }
        
        if(str.equals("}") && !ignore)
    	{
    		remove(offs - 5, 5);
    	}
    }
    
    /**
     * @param element Tree element
     * @param builder space builder
     * @return number of spaces before the first none text character
     */
    private int spacesBeforeLine(Element element, StringBuilder builder)
    {
		
		int count = 0;
		Element current = element.getElement(count);

		if(!current.getName().equals("content"))
		{
				
			while(current != null && SyntaxType.valueOf(current.getName()) == SyntaxType.DELIMITER && current.getAttributes().getAttribute("string").toString().equals(" "))
			{
				builder.append(" ");
				count++;
				current = element.getElement(count);
			}
		}
		
		return count;
    }
    
    /**
     * @param element
     * @return position of caret
     */
    private int getCaretIndex(Element element)
    {
		int caret = WorkSpaceManager.textAreaManager.getSelectedEditor().getCaretPosition();
		int caretIndex = element.getElementIndex(caret);
		
		return caretIndex;
    }
}
